package filecache

import (
	"encoding/gob"
	"encoding/json"
	"os"
	"path/filepath"
	"sync"
	"time"

	Log "github.com/cihub/seelog"
)

var (
	checkDbMap = map[string]*FileCache{} //缓存库集合 *FileCache
	loadLocks  sync.Mutex                // 读写锁
)

// 缓存项结构
type cacheItem struct {
	Value      interface{}
	LastAccess time.Time // 最后访问时间
	Expiration time.Time // 过期时间
}

// 复合键类型
type CompositeKey struct {
	Key1 string
	Key2 string
	// 可根据需要添加更多字段
}

// 缓存系统结构
type FileCache struct {
	Items           map[interface{}]*cacheItem  // 支持多种键类型
	KeyMap          map[interface{}]interface{} // 键到主键的映射
	LifeTime        time.Duration               //缓存寿命
	AutoProlongLife bool                        //自动延长寿命
	FilePath        string
	fileStat        os.FileInfo  // 记录文件状态
	mu              sync.RWMutex //操作锁
}

// 取缓存对象
func GetCheck(dbName string, lifeTimeSecond int64, autoProlongLife bool) (*FileCache, error) {
	loadLocks.Lock()
	defer loadLocks.Unlock()

	if db, ok := checkDbMap[dbName]; ok {
		dbFile := filepath.Join(dbName)
		_, err := os.Stat(dbFile)
		if !os.IsNotExist(err) { //文件存在则说明没有被清理,可以继续用
			return db, nil
		}

		delete(checkDbMap, dbName) //文件已经不存在,删除
	}

	db, err := NewCache(dbName, lifeTimeSecond, autoProlongLife)
	if err != nil {
		return nil, err
	}

	checkDbMap[dbName] = db // 存储最终结果

	return db, nil
}

// 确保目录存在
func ensureDir(path string) error {
	dir := filepath.Dir(path)
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		return os.MkdirAll(dir, 0755) // 创建所有不存在的目录
	}

	return nil
}

// 创建新缓存实例
func NewCache(filePath string, lifeTimeSecond int64, autoProlongLife bool) (*FileCache, error) {
	// 确保目录存在
	if err := ensureDir(filePath); err != nil {
		Log.Error("创建目录失败:", err)
		return nil, err
	}

	var lifeTime time.Duration
	if lifeTimeSecond == 0 { // 如果没有设置缓存寿命，则使用默认值
		lifeTime = -1 // 默认为无限期
	} else {
		lifeTime = time.Duration(lifeTimeSecond) * time.Second
	}

	cache := &FileCache{
		Items:           make(map[interface{}]*cacheItem),
		KeyMap:          make(map[interface{}]interface{}),
		FilePath:        filePath,
		AutoProlongLife: autoProlongLife,
		LifeTime:        lifeTime,
	}

	cache.loadFromFile() // 初始加载数据

	cache.updateFileStat() // 记录初始文件状态

	return cache, nil
}

// 更新文件状态信息
func (c *FileCache) updateFileStat() {
	fileInfo, err := os.Stat(c.FilePath)
	if err == nil {
		c.fileStat = fileInfo
	} else {
		c.fileStat = nil
	}
}

// 检查文件状态变化
func (c *FileCache) checkFileState() {
	currentStat, err := os.Stat(c.FilePath)

	// 情况1: 文件被删除
	if os.IsNotExist(err) {

		c.mu.Lock()
		defer c.mu.Unlock()

		// 清空内存缓存
		c.Items = make(map[interface{}]*cacheItem)

		// 确保目录存在
		if err := ensureDir(c.FilePath); err != nil {
			Log.Error("创建目录失败:", err)
			return
		}

		// 创建新文件
		if file, err := os.Create(c.FilePath); err == nil {
			file.Close()
			c.updateFileStat()
		} else {
			Log.Error("创建文件失败:", err)
		}

		return
	}

	// 情况2: 文件被替换或修改
	if err == nil && (c.fileStat == nil ||
		(currentStat.ModTime() != c.fileStat.ModTime() || currentStat.Size() != c.fileStat.Size())) {

		c.mu.Lock()
		defer c.mu.Unlock()

		// 重新加载文件内容
		c.Items = make(map[interface{}]*cacheItem)
		c.KeyMap = make(map[interface{}]interface{})

		c.loadFromFile()
		c.fileStat = currentStat
	}
}

// 设置缓存值（支持多种键类型）
func (c *FileCache) Set(value interface{}, mainKey interface{}, otherKeys ...interface{}) *FileCache {
	if value == nil {
		return c
	}

	c.checkFileState()

	var expiration time.Time
	if c.LifeTime > 0 {
		expiration = time.Now().Add(c.LifeTime)
	} else {
		expiration, _ = time.Parse("2006-01-02 15:04:05", "2599-01-02 15:04:05")
	}

	c.mu.Lock()
	defer c.mu.Unlock()

	// 将 value 转换为 json 字符串
	var str string
	if _, ok := value.(string); ok { // value 已经是字符串
		str = value.(string)
	} else { // value 不是字符串
		jsonValue, err := json.Marshal(value)
		if err != nil {
			Log.Error("JSON 序列化失败:", err)
			return c
		}

		str = string(jsonValue)
	}

	cacheItem := &cacheItem{
		Value:      str,
		LastAccess: time.Now(),
		Expiration: expiration,
	}

	c.Items[mainKey] = cacheItem

	if len(otherKeys) < 1 { // 如果没有其他键，则直接返回
		go c.Save()
		return c
	}

	for _, key := range otherKeys {
		c.KeyMap[key] = mainKey
	}

	go c.Save()

	return c
}

// 获取缓存值
func (c *FileCache) Get(key interface{}, result any) (any, bool) {
	c.checkFileState()

	c.mu.RLock()
	item, exists := c.Items[key]

	if !exists {
		mainKey, ok := c.KeyMap[key] //到映射找主键
		if !ok {
			c.mu.RUnlock()
			return nil, false
		}

		item, exists = c.Items[mainKey]
		if !exists {
			c.mu.RUnlock()
			return nil, false
		}
	}

	c.mu.RUnlock()

	// 检查是否过期
	if time.Now().After(item.Expiration) {
		c.mu.Lock()
		delete(c.Items, key)
		c.mu.Unlock()
		return nil, false
	}

	// 更新最后访问时间
	c.mu.Lock()
	item.LastAccess = time.Now()

	if c.AutoProlongLife && c.LifeTime > 0 { //如果自动延长寿命则
		item.Expiration = time.Now().Add(c.LifeTime) //延长到期时间
	}

	c.mu.Unlock()

	if result == nil {
		return item.Value, true
	}

	if str, ok := result.(string); ok {
		return str, true
	}

	// 反序列化 JSON 字符串
	if err := json.Unmarshal([]byte(item.Value.(string)), result); err != nil {
		Log.Error("JSON 反序列化失败:", err)
		return nil, false
	}

	return result, true
}

// 获取缓存大小
func (c *FileCache) Size() int {
	return len(c.Items)
}

// 获取缓存值
func (c *FileCache) HasKey(key interface{}, lifeTime bool) bool {
	c.checkFileState()

	c.mu.Lock()
	defer c.mu.Unlock()

	if item, ok := c.Items[key]; ok {
		if lifeTime {
			item.Expiration = time.Now().Add(c.LifeTime) //延长到期时间
		}

		return true
	}

	mainKey, ok := c.KeyMap[key] //到映射找主键
	if !ok {
		return false
	}

	if item, ok := c.Items[mainKey]; ok {
		if lifeTime {
			item.Expiration = time.Now().Add(c.LifeTime) //延长到期时间
		}
		return true
	}

	return false
}

// 更新最后访问时间
func (c *FileCache) UpdateAccessTime(key interface{}) {
	c.checkFileState()

	c.mu.Lock()
	defer c.mu.Unlock()

	if item, exists := c.Items[key]; exists {
		item.LastAccess = time.Now()
		go c.Save()
		return
	}

	mainKey, ok := c.KeyMap[key] //到映射找主键
	if !ok {
		return
	}

	if val, ok := c.Items[mainKey]; ok {
		val.LastAccess = time.Now()
		go c.Save()
		return
	}
}

// 更新过期时间
func (c *FileCache) UpdateExpiration(key interface{}, ttl time.Duration) {
	c.checkFileState()

	c.mu.Lock()
	defer c.mu.Unlock()

	if item, exists := c.Items[key]; exists {
		item.Expiration = time.Now().Add(ttl)
		go c.Save()
		return
	}

	mainKey, ok := c.KeyMap[key] //到映射找主键
	if !ok {
		return
	}

	if val, ok := c.Items[mainKey]; ok {
		val.Expiration = time.Now().Add(ttl)
		go c.Save()
		return
	}
}

// 删除缓存项
func (c *FileCache) Del(key interface{}) (interface{}, bool) {
	c.checkFileState()

	c.mu.Lock()
	defer c.mu.Unlock()

	item, ok := c.Items[key]
	if ok {
		delete(c.Items, key)
		for k, val := range c.KeyMap { //清理映射
			if val == key {
				delete(c.KeyMap, k)
			}
		}

		return item, true
	}

	mainKey, ok := c.KeyMap[key] //到映射找主键
	if !ok {
		return nil, false
	}

	item = c.Items[mainKey]
	delete(c.Items, mainKey)
	for k, val := range c.KeyMap { //清理映射
		if val == mainKey {
			delete(c.KeyMap, k)
		}
	}

	if item == nil { //主键对应的值被删除了
		return nil, false
	}

	go c.Save()

	return item, true
}

// 保存缓存到文件
func (c *FileCache) Save() error {
	c.checkFileState()

	// 确保目录存在
	if err := ensureDir(c.FilePath); err != nil {
		Log.Error("创建目录失败:", err)
		return err
	}

	c.mu.Lock()
	defer c.mu.Unlock()

	file, err := os.Create(c.FilePath)
	if err != nil {
		return err
	}
	defer file.Close()

	encoder := gob.NewEncoder(file)
	if err := encoder.Encode(c); err != nil {
		Log.Error("编码缓存数据异常:", err)
		return err
	}

	// 更新文件状态
	c.updateFileStat()

	return nil
}

// 从文件加载缓存
func (c *FileCache) loadFromFile() {
	// 确保目录存在
	if err := ensureDir(c.FilePath); err != nil {
		Log.Error("创建目录失败:", err)
		return
	}

	file, err := os.Open(c.FilePath)
	if err != nil {
		return // 文件不存在时忽略
	}
	defer file.Close()

	fileInfo, err := file.Stat()
	if err != nil || fileInfo.Size() == 0 { // 文件为空或获取状态失败
		return
	}

	decoder := gob.NewDecoder(file)
	if err = decoder.Decode(&c); err != nil {
		Log.Error("解码文件异常:", err)
	}
}

// 清理过期项目
func (c *FileCache) Cleanup() {
	c.checkFileState()

	c.mu.Lock()
	defer c.mu.Unlock()

	now := time.Now()
	for key, item := range c.Items {
		if !now.After(item.Expiration) {
			continue
		}

		delete(c.Items, key)

		for k, val := range c.KeyMap { //清理映射
			if val == key {
				delete(c.KeyMap, k)
			}
		}
	}
}

// 获取所有键值对（用于调试）
func (c *FileCache) GetAll() map[interface{}]interface{} {
	c.checkFileState()

	c.mu.RLock()
	defer c.mu.RUnlock()

	result := make(map[interface{}]interface{})
	for key, item := range c.Items {
		result[key] = item.Value
	}

	return result
}

// 清理所有缓存
func (c *FileCache) Clear() {
	c.checkFileState()

	c.mu.RLock()
	defer c.mu.RUnlock()

	os.Remove(c.FilePath)

	c.Items = make(map[interface{}]*cacheItem)
	c.KeyMap = make(map[interface{}]interface{})
}
